import json
import os
import re
import copy


def compile(conf_path):

    with open(conf_path ,"r") as f:
        conf = json.load(f)
    print("conf", conf)

    main_callbacks = {"OnGameStart":{"params":"", "lines":[]}, 
                    "OnTick":{"params":"", "lines":[]}, 
                    "OnFrame":{"params":"", "lines":[]}, 
                    "OnLateFrame":{"params":"", "lines":[]}, 
                    "OnSecond":{"params":"", "lines":[]},
                    "OnChatInput":{"params":"message", "lines":[]},
                    "OnPlayerSpawn":{"params":"player, character", "lines":[]},
                    "OnCharacterSpawn":{"params":"character", "lines":[]},
                    "OnCharacterReloaded":{"params":"character", "lines":[]},
                    "OnCharacterDie":{"params":"victim, killer, killerName", "lines":[]},
                    "OnCharacterDamaged":{"params":"victim, killer, killerName, damage", "lines":[]},
                    "OnPlayerJoin":{"params":"player", "lines":[]},
                    "OnPlayerLeave":{"params":"player", "lines":[]},
                    "OnNetworkMessage":{"params":"sender, message", "lines":[]}, 
                    "OnButtonClick":{"params":"buttonName", "lines":[]}}

    def extract_function_details(input_string):
        pattern = r'function\s+(\w+)\s*\(([^)]*)\)'  # 匹配 function FunctionName(params) 格式
        match = re.search(pattern, input_string)
        if match:
            function_name = match.group(1)
            params = match.group(2)
            return function_name, params
        return None
    def parse_code_file(mode_lines):
        braces = 0
        fields = []
        functions = {}
        current_function = None
        current_function_name = None
        for line in mode_lines:
            left_brace = line.count("{")
            right_brace = line.count("}")
            braces += left_brace - right_brace
            if braces == 0:
                continue
            if braces == 1 and not line.strip().startswith("function "):
                if "=" in line:
                    fields.append(line)
                    continue
            if line.strip().startswith("function "):
                current_function_name, current_function_params = extract_function_details(line)
                current_function = []
                functions[current_function_name] = {
                    "params": current_function_params,
                    "lines":current_function,
                }
                if line.strip()[-1] == "{":
                    current_function.append("    {\n")
            elif current_function is not None:
                current_function.append(line)
        callbacks = copy.deepcopy(main_callbacks)
        new_functions = {}
        for k,v in functions.items():
            if k in callbacks:
                callbacks[k] = v
            else:
                new_functions[k] = v
        return fields, new_functions, callbacks
            
    def read_mode_content(conf):
        base_path = conf["BasePath"]
        base_mode = conf["BaseMode"]
        mode_path = os.path.join(base_path, "modes", f"{base_mode}.txt")
        with open(mode_path,"r") as f:
            mode_lines = [l for l in f.readlines()]
        print(mode_lines)
        return parse_code_file(mode_lines)
    if conf["BaseMode"] == "":
        fields, functions, callbacks = [], {}, copy.deepcopy(main_callbacks)
    else:
        fields, functions, callbacks = read_mode_content(conf)
    print("fields", fields)
    print("functions", functions)
    print("callbacks", callbacks)


    def add_callbacks(class_name, mod_lines, callbacks):
        for line in mod_lines:
            if line.strip().startswith("function"):
                function_name, function_params = extract_function_details(line)
                if function_name in callbacks:
                    callbacks_val = callbacks[function_name]
                    function_params, lines = callbacks_val["params"], callbacks_val["lines"]
                    if len(lines) == 0:
                        lines.extend(['    {\n', '    }\n'])
                    if "extra_lines" not in callbacks_val:
                        callbacks_val["extra_lines"] = []
                    extra_lines = callbacks_val["extra_lines"]
                    if function_name == "OnNetworkMessage":
                        extra_lines.append(f"        if({class_name}.OnNetworkMessage(" + function_params +")){return;}\n")
                    else:
                        extra_lines.append(f"        {class_name}.{function_name}({function_params});\n")

    def GetMods(base_path, mods):
        results = set()
        def GetDepMods(parent_name, _mods):
            for mod_name in _mods:
                if parent_name is not None:
                    mod_name = parent_name + "/" + mod_name
                if mod_name in results:
                    continue
                results.add(mod_name)
                mod_path = os.path.join(base_path, mod_name)
                with open(os.path.join(mod_path, "mod.json"), "r") as f:
                    mod_desc = json.load(f)
                if "DepMods" in mod_desc:
                    GetDepMods(None, mod_desc["DepMods"])
                if "SubMods" in mod_desc:
                    GetDepMods(mod_name, mod_desc["SubMods"])
        GetDepMods(None, mods)
        return [s for s in results]
            

    def read_mods_content(conf, functions, callbacks, fields):
        callbacks = copy.deepcopy(callbacks)
        functions = copy.deepcopy(functions)
        fields = copy.deepcopy(fields)
        for i in range(len(fields)):
            line = fields[i]
            if line.strip().startswith("Description"):
                new_desc = conf["Description"]
                fields[i] = f'    Description = "{new_desc}";\n'
                break
        fields.extend(["\n", "    #Extra Global Variable\n"])
        base_path = conf["BasePath"]
        mod_strs = []
        for mod_name in GetMods(base_path, conf["Mods"]): 
            mod_path = os.path.join(base_path, mod_name)
            with open(os.path.join(mod_path, "mod.json"), "r") as f:
                mod_desc = json.load(f)
            mod_strs.append("# Mod Module: " + mod_name + " " + mod_desc["Name"] + "\n")
            if len(mod_desc["Desc"]) > 0:
                mod_strs.append("/*\n")
                mod_strs.extend(mod_desc["Desc"])
                mod_strs.append("*/\n")
            if (mod_desc["GlobalVar"]):
                fields.append(f"    #Global variable for {mod_name} mod\n")
                fields.extend(f"    {l}\n" for l in mod_desc["GlobalVar"])
                fields.append("\n")
            for script_filename in os.listdir(mod_path):
                if not script_filename.endswith(".txt"):
                    continue
                script_path = os.path.join(mod_path, script_filename)
                with open(script_path,"r") as f:
                    mod_lines = [l for l in f.readlines()]
                class_type, class_name = [l.strip() for l in mod_lines[0].replace("{","").strip().split(" ") if l]
                class_type, class_name
                if class_type == "extension":
                    add_callbacks(class_name, mod_lines, callbacks)
                mod_strs.extend(mod_lines)
                mod_strs.append("\n\n")
        callbacks_strs = []
        for k,v in callbacks.items():
            if len(v["lines"]) == 0:
                continue
            function_params = v["params"]
            callbacks_strs.append(f"    function {k}({function_params})\n")
            callbacks_strs.append("    {\n")
            if "extra_lines" in v:
                callbacks_strs.extend(v["extra_lines"])
            callbacks_strs.extend(v["lines"][1:])
        function_strs = []
        for k,v in functions.items():
            if len(v["lines"]) == 0:
                continue
            function_params = v["params"]
            function_strs.append(f"    function {k}({function_params})\n")
            function_strs.append("    {\n")
            if "extra_lines" in v:
                function_strs.extend(v["extra_lines"])
            function_strs.extend(v["lines"][1:])
        return mod_strs, function_strs, callbacks_strs, fields
    mod_strs, function_strs, callbacks_strs, fields_strs = read_mods_content(conf, functions, callbacks,fields)
    print("mod_strs", mod_strs)
    print("function_strs", function_strs)
    print("callbacks_strs", callbacks_strs)
    print("fields_strs", fields_strs)

    main_strs = ["class Main\n{\n"]
    main_strs.extend(fields_strs)
    main_strs.append("    # Callback Functions\n\n")
    main_strs.extend(callbacks_strs)
    main_strs.append("    # Other Functions\n\n")
    main_strs.extend(function_strs)
    main_strs.extend("}\n")
    main_strs.append("# Extra Modules\n\n")
    main_strs.extend(mod_strs)

    completed_script_str = "".join(main_strs)
    mode_name = conf["Name"]
    output_path = os.path.join(conf["OutputPath"], f"{mode_name}.txt")
    with open(output_path, 'w', encoding='utf-8') as f:
        f.write(completed_script_str)
    with open("temp.txt", 'w', encoding='utf-8') as f:
        f.write(completed_script_str)

    if ("ReleasePath" in conf):
        os.makedirs(conf["ReleasePath"], exist_ok=True)
        output_path = os.path.join(conf["ReleasePath"], f"{mode_name}.txt")
        with open(output_path, 'w', encoding='utf-8') as f:
            f.write(completed_script_str)


